package control;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import model.CivilPlace;
import model.Empire;
import model.GameBoard;
import model.Player;
import model.Province;
import model.SeaArea;
import vinciappli.CardId;
import vinciappli.ProvinceSubType;
import vinciappli.StaticParams;


public class ControllerGameBoard {

    private GameBoard gameBoard = new GameBoard();
    /* Number of the player who's playing at the moment */
    private int currentPlayer;
    /* Total amount of points won thanks to card "esclavage" = amount of player's conquests during his turn */
    private int assaultBonusPts = 0;
    /* Amount of players'round since the beginning of the game */
    private int nbRound = 0;

    private Player memoPlayer;

    private Set<Province> newProvinces = new HashSet<Province>();

   /* Collection of the troops from other players than the current one, who have been attacked and should be replaced on their related empire */
    private Map<Player, Integer> IdleTroops = new HashMap<Player, Integer>();

    public ControllerGameBoard() {
        
    }
    

    public void ini ()
    {
        ControllerXML.getINSTANCE().iniXML();
    }

    /**
     * Start a game
     * - Fill the gameboard 6 places civilizations area
     * - Set up each province with a fort to represent the depressed previous forces
     */
    public void startGame()
    {
        gameBoard.fillCivilizationPlaces();
        gameBoard.setPreviousForces();
    }

    /**
     * Calculation effects from the position of the civilization selected by the player
     * <br>- Add bonus points for unchosen civilizations
     * <br>- Fill up the civilizations places
     * @param position : Position of the civilization on the gameboard (between 1 and 6)
     */
    public void civilizationPickUp(int position)
    {
        this.addBonusPtsToUnchosenCivilization(position);
        gameBoard.getCivilizations().remove(position);
        gameBoard.fillCivilizationPlaces();
    }

    /**
     * Add bonus points for unselected civilizations
     * @param position : Position of the selected civilization on the gameboard by a player
     */
    public void addBonusPtsToUnchosenCivilization(int position)
    {
        int i = 0;
        for(CivilPlace cp : gameBoard.getCivilizations())
        {
            if(i < position)
            {
                cp.setBonusPts(cp.getBonusPts() + 2);
            }
            i++;
        }
    }

    /**
     * Set the current playing figures (player, round)
     * @return : Round number
     */
    public int nextRound()
    {
        if(this.getNumCurrentPlayer() == ControllerPrincipal.getINSTANCE().getControllerPlayer().getListPlayers().size()-1)
        {
            currentPlayer = 0;
            nbRound++;
        }
        else
        {
            currentPlayer++;
        }
        assaultBonusPts = 0;
        return nbRound;
    }

    /**
     * Return the total amount of troops needed to conquer a defined province
     * @param from : Province where the assailant player is coming from (could be null if the empire has got no province at all (new empire)
     * @param destination : Province the assailant player is going to conquer
     * @param assailant : Player who's playing and going to conquer
     * @return : The cost of the conquest for the assailant player
     */
    public int getConquestCost(Province from, Province destination, Player assailant)
    {
        int fieldCost = destination.getConquestNeededTroops();
        int occupationCost = destination.getNeededTroopsToConquest();
        int specialCardAdvantage = 0;
        Player assailed = ControllerPrincipal.getINSTANCE().getControllerPlayer().getPlayerFromProvince(destination);

        if(from != null && from != gameBoard.getGameBoardEdge())
        {
            if(from.getFieldType().equals(StaticParams.PROVINCE_FIELDTYPE_MOUNTAIN))
            {
                fieldCost += -1;
            }

            if(assailant.getEmpire(false).getCivilization().isComposedOf(CardId.galeres))
            {
                if(from.getSubtypes().contains(ProvinceSubType.cotier) && destination.getSubtypes().contains(ProvinceSubType.cotier))
                {
                    specialCardAdvantage += -1;
                }
            }

            if((assailant.getEmpire(false).getCivilization().isComposedOf(CardId.fortsDec))
                    && from.getTerritoryOccupation().containsKey(StaticParams.PAWN_TYPE_FORT))
            {
                if(from.getTerritoryOccupation().get(StaticParams.PAWN_TYPE_FORT) == 1)
                {
                    specialCardAdvantage += -1;
                }
            }
        }
        if(assailant.getEmpire(false).getCivilization().isComposedOf(CardId.montagnard))
        {
            if(destination.getFieldType().equals(StaticParams.PROVINCE_FIELDTYPE_MOUNTAIN))
            {
                specialCardAdvantage += -1;
            }
        }
        if(assailant.getEmpire(false).getCivilization().isComposedOf(CardId.armes))
        {
            specialCardAdvantage += -1;
        }

        if(assailed != null)
        {
            if(assailed.getEmpire(false).getCivilization().isComposedOf(CardId.millice))
            {
                specialCardAdvantage += 1;
            }

            if((assailed.getEmpire(false).getCivilization().isComposedOf(CardId.fortsDec))
                && destination.getTerritoryOccupation().containsKey(StaticParams.PAWN_TYPE_FORT))
            {
                if(from.getTerritoryOccupation().get(StaticParams.PAWN_TYPE_FORT) == 1)
                {
                    specialCardAdvantage += 1;
                }
            }
        }

        if(assailant.getEmpire(false).getCivilization().isComposedOf(CardId.specialisation))
        {
            specialCardAdvantage = specialCardAdvantage * 2;
        }

        return fieldCost + occupationCost + specialCardAdvantage;
    }

    /**
     * Set an empire as depressed physically on the gameboard
     * @param e : Empire which is going to be depressed
     */
    public void goDepressEmpire(Empire e)
    {
        Player p = ControllerPlayer.getPlayerFromEmpire(e);
        if(e.getListProvinces() != null)
        {
            for(Province pr : e.getListProvinces())
            {
                p.setAvailableTroopsFromCurrentNumber(pr.getTerritoryOccupation().get(p.getNickname()) - 1);
                pr.getTerritoryOccupation().put(p.getNickname(), 1);
                if(!p.getEmpire(false).getCivilization().isComposedOf(CardId.fortsDec))
                {
                    pr.getTerritoryOccupation().put(StaticParams.PAWN_TYPE_FORT, 0);
                }
            }
        }
        if(!e.getCivilization().getCaracteristic1().isValidEmpireDown())
        {
            gameBoard.getBag().getListCards().add(e.getCivilization().getCaracteristic1());
            e.getCivilization().setCaracteristic1(null);
        }
        if(!e.getCivilization().getCaracteristic2().isValidEmpireDown())
        {
            gameBoard.getBag().getListCards().add(e.getCivilization().getCaracteristic2());
            e.getCivilization().setCaracteristic2(null);
        }
        
        e.setDepressed(true);
    }

    /**
     * Remove an empire from the game, reset all of its provinces, and put back civilization cards in the bag
     * @param p
     */
    public void removeEmpire(Player p)
    {
        Empire oldDepressedEmpire = p.getEmpire(true);
        gameBoard.getBag().getListCards().add(oldDepressedEmpire.getCivilization().getCaracteristic1());
        gameBoard.getBag().getListCards().add(oldDepressedEmpire.getCivilization().getCaracteristic2());
        for(Province pr : oldDepressedEmpire.getListProvinces())
        {
            pr.getTerritoryOccupation().remove(p.getNickname());
            pr.getTerritoryOccupation().put(StaticParams.PAWN_TYPE_FORT, 1);
        }
        p.getEmpires().remove(p.getEmpire(true));
    }
    
    /**
     * Find all provinces that could be conquered by the player active empire
     * @param p : Player which wants to know all conquest choices he has from his active empire
     * @return : Set containing all provinces that could be conquered by the player active empire
     */
    public Set<Province> getPotentialProvinces(Player p)
    {
        Set<Province> potentialProvinces = new HashSet<Province>();
        Set<Province> friendlyProvinces = new HashSet<Province>();

        if(!p.getEmpire(false).getListProvinces().isEmpty())
        {
            for(Province pr : p.getEmpire(false).getListProvinces())
            {
                potentialProvinces.addAll(pr.getNearbyProvinces());
                if(p.canGoThroughSeaArea())
                {
                    for(SeaArea sa : pr.getNearbySeaAreas())
                    {
                        potentialProvinces.addAll(sa.getNearbyProvincesThroughSeas());
                    }
                }
                potentialProvinces.removeAll(p.getEmpire(false).getListProvinces());
            }
        }
        else
        {
           potentialProvinces = ControllerPrincipal.getINSTANCE().getControllerGameBoard().getGameBoard().getStartingProvinces(p);
        }

        if(p.getEmpire(true) != null)
        {
            if(!p.getEmpire(false).getCivilization().isComposedOf(CardId.heritage))
            {
                potentialProvinces.removeAll(p.getEmpire(true).getListProvinces());
            }
        }

        if(p.getFriend() != null)
        {
            for(Province pr : potentialProvinces)
            {
                if(p.getFriend().equals(ControllerPrincipal.getINSTANCE().getControllerPlayer().getPlayerFromProvince(pr)))
                {
                    friendlyProvinces.add(pr);
                }
            }
            potentialProvinces.removeAll(friendlyProvinces);
        }

        return potentialProvinces;
    }

    public Player pickIdleTroopsPlayer()
    {
        Player p = null;
        for(Entry<Player, Integer> entry : IdleTroops.entrySet())
        {
            p = entry.getKey();
            break;
        }
        IdleTroops.remove(p);
        return p;
    }

    /**
     * Calculation of all points won during a player's turn
     * @param p : Current player
     * @return
     */
    public int ptsCalculation(Player p)
    {

        int iniPts = 0;
        if(p.getEmpire(true) != null)
        {
            iniPts = p.getEmpire(true).getListProvinces().size() - p.getEmpire(true).getNbProvinceByType(StaticParams.PROVINCE_FIELDTYPE_MOUNTAIN);
        }
        if(p.getEmpire(false) != null)
        {
            iniPts += p.getEmpire(false).getListProvinces().size() - p.getEmpire(false).getNbProvinceByType(StaticParams.PROVINCE_FIELDTYPE_MOUNTAIN);
        }
        int cardAdvantage = gameBoard.cardAdvantageCalculation(p);

        return iniPts + cardAdvantage;
    }


    public void setAssaultBonusPtsFromCurrentValue(int val) {
        this.assaultBonusPts += val;
    }

    /**
     * Special points at the beginning of the game, related to how many players are registered
     * @return : Amount of points given at the beginning of the game
     */
    public int getSpecialPts()
    {
        int pts = 0;
        switch(ControllerPrincipal.getINSTANCE().getControllerPlayer().getListPlayers().size())
        {
            case 3 :
                pts = 8;
            break;
            case 4 :
                pts = 6;
            break;
            case 5 :
                pts = 4;
            break;
            case 6 :
                pts = 3;
            break;
        }
        return pts;
    }

    /***************************** GETTERS & SETTERS **************************/

     public Player getCurrentPlayer() {
         return ControllerPrincipal.getINSTANCE().getControllerPlayer().getListPlayers().get(currentPlayer);
     }

     public void setCurrentPlayer(Player p)
    {
         currentPlayer = ControllerPrincipal.getINSTANCE().getControllerPlayer().getListPlayers().indexOf(p);
     }

     public int getNumCurrentPlayer() {
         return currentPlayer;
     }

     public void setNumCurrentPlayer(int num) {
         this.currentPlayer = num;
     }

    public void setNextPlayer() {
        this.currentPlayer ++;
    }
    
    public GameBoard getGameBoard() {
        return gameBoard;
    }

    public int getNbRound() {
        return nbRound;
    }

    public int getAssaultBonusPts() {
        return assaultBonusPts;
    }

    public Map<Player, Integer> getIdleTroops() {
        return IdleTroops;
    }

    public Set<Province> getNewProvinces()
    {
        return newProvinces;
    }

    public Player getMemoPlayer()
    {
        return memoPlayer;
    }

    public void setMemoPlayer(Player memoPlayer)
    {
        this.memoPlayer = memoPlayer;
    }
}

